﻿using QDasConverter.Core;
using QDasConverter.Utils;
using Spire.Xls;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace QDasConverter
{
    /// <summary>
    /// 项目中全局的变量和函数。
    /// </summary>
    public class Common
    {
        /// <summary>
        /// 日志显示等级，默认值为 INFO，可以在程序初始化时根据配置文件修改。
        /// </summary>
        public static LogType LogLevel = LogType.INFO;
        public static IniAccess ia = new IniAccess();


        internal static void Initialize()
        {
            IniAccess.DefaultConfigFile = Path.Combine(Application.StartupPath, "config.ini");
            var ia = new IniAccess();
            ia.WriteValue("TEST", "Hello");
            try
            {
                LogLevel = (LogType)Enum.Parse(typeof(LogType), ia.ReadString("LogLevel", "INFO"));
            }
            catch { }

            // 测试模式的使用方法
            ConvertBase.DebugMode = File.Exists("ATestFile.debug");
        }

        /// <summary>
        /// 判断指定字符串s，从 start 开始的连续 len 个字符是否是数字。
        /// </summary>
        /// <param name="id"></param>
        /// <param name="start"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static bool IsNumber(string id, int start, int len)
        {
            bool isNumber = true;
            for (int i = 0; i < len; i++)
            {
                if (!char.IsDigit(id[start + i]))
                    isNumber = false;
            }
            return isNumber;
        }

        /// <summary>
        /// 将日志内容追加至文件末尾。
        /// </summary>
        /// <param name="log">日志内容。</param>
        /// <param name="logfile">文件列表。</param>
        public static void Error(string log, string logfile = "application.log")
        {
            try
            {
                using (StreamWriter sw = new StreamWriter(logfile, true, Encoding.UTF8))
                {
                    sw.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss} {log}");
                }
            }
            catch { }
        }

        /// <summary>
        /// 如果文件名称中包括非法字符（\/:?\"<>|），则使用指定字符替换（默认为下划线）。
        /// </summary>
        /// <param name="filename">待校验的文件名称。</param>
        /// <param name="replacement">非法字符的替换字符。</param>
        public string VarifyFilename(string filename, string replacement = "_")
        {
            string illegals = "\\/:?\"<>|";
            string newfilename = Path.GetFileName(filename);
            for (int i = 0; i < illegals.Length; i++)
                newfilename = newfilename.Replace(illegals[i], '_');
            return newfilename;
        }

        /// <summary>
        /// 创建目录，如果创建失败，返回为false
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static bool CreateDirectoryIfNotExisted(string path, bool popErrorWindow = true)
        {
            // 已经存在则返回为true
            if (Directory.Exists(path))
                return true;
            Directory.CreateDirectory(path);
            if (!Directory.Exists(path))
            {
                MessageBox.Show($"文件夹 {path} 创建失败。", "路径创建失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 从指定的Excel中提取表单，如果读取失败会写日志，同时返回为 null。
        /// </summary>
        /// <param name="inpath">输入Excel文件。</param>
        /// <param name="index">表单索引，下标从0开始。</param>
        /// <returns></returns>
        public static Worksheet LoadWorkSheet(string inpath, int index)
        {
            Worksheet sheet = null;
            Workbook workbook = new Workbook();
            try
            {
                workbook.LoadFromFile(inpath);
                if (workbook.Worksheets.Count == 0 || workbook.Worksheets.Count <= index)
                    Error("文件索引错误，读取表单内容为空");
                else
                    sheet = workbook.Worksheets[index]; // 读取成功后在这里赋值，此时才不为空。
            }
            catch (Exception ex1)
            {
                Error("文件读取失败，原因：" + ex1.Message);
            }
            finally
            {
                try
                {
                    // 关闭文件，可能不需要操作，先保留

                }
                catch (Exception ex2) { Error($"{ inpath} 文件关闭失败，原因：" + ex2.Message); }
            }

            return sheet;
        }


        /// <summary>
        /// 复制文件，注意几点：
        /// 1、如果源文件不存在，写文件不存在错误。
        /// 2、如果目标文件已经存在，加时间编号，如file.xls -> file20120519092732.dfq
        /// </summary>
        /// <param name="scr">源文件</param>
        /// <param name="dst">目标文件</param>
        public static void MoveFile(string scr, string dst)
        {
            try
            {
                if (!File.Exists(scr))
                    AddLog("FILE\t复制时源文件: " + scr + " 不存在。");

                if (File.Exists(dst))
                {
                    dst = AppendTimeToFileName(dst);
                }

                File.Copy(scr, dst);

                File.Delete(scr);
            }
            catch (Exception e1)
            {
                AddLog("FILE\t" + e1.Message);
            }
        }

        /// <summary>
        /// 根据Converter的名称，初始化转换器。
        /// </summary>
        /// <param name="converterKey"></param>
        /// <returns></returns>
        public static ConvertBase ConverterFactory(string converterKey)
        {
            TsingShanConverters ct = TsingShanConverters.C2021T02_cvinet;
            try
            {
                ct = (TsingShanConverters)Enum.Parse(typeof(TsingShanConverters), converterKey);
            }
            catch (Exception ex) { Console.WriteLine(ex.Message); }

            switch (ct)
            {
                case TsingShanConverters.C2021T01_atlas: return new C2021T01_Atlas();
                case TsingShanConverters.C2021T02_cvinet: return new C2021T02_cvinet();
                case TsingShanConverters.C2021T03_CTL: return new C2021T03_CTL();
                case TsingShanConverters.C2021T04_DisenNVH: return new C2021T04_DisenNVH();
                case TsingShanConverters.C2021T05_DisenConsole: return new C2021T05_DisenConsole();
                case TsingShanConverters.C2021T06_HoutaNVH: return new C2021T06_HoutaNVH();
                case TsingShanConverters.C2021T07_HoutaConsole: return new C2021T07_HoutaConsole();
                case TsingShanConverters.C2021T08_Kisler: return new C2021T08_Kisler();
                case TsingShanConverters.C2021T20_ComaAtlas: return new C2021T20_ComaAtlas();
                case TsingShanConverters.C2021T09_Excel: return new C2021T09_Excel();
                case TsingShanConverters.C2021T10_SPH: return new C2021T10_SPH();
                case TsingShanConverters.C2021T11_TDMS: return new C2021T11_TDMS();
                default: return new ConvertBase();
            }
        }


        /// <summary>
        /// 给指定文件名加时间戳。
        /// </summary>
        /// <param name="dst">目标地址。</param>
        /// <returns></returns>
        public static string AppendTimeToFileName(string dst)
        {
            try
            {
                string path = Path.GetDirectoryName(dst);
                string filename = Path.GetFileNameWithoutExtension(dst);
                string ext = Path.GetExtension(dst);
                return path + "\\" + filename + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ext;
            }
            catch { }

            return dst;
        }

        /// <summary>
        /// 根据输入字符串解析时间，如果格式不正确，返回当前时间。
        /// </summary>
        /// <param name="datetime_string"></param>
        /// <returns></returns>
        public static DateTime ParseDatetime(string datetime_string)
        {
            try
            {
                return DateTime.Parse(datetime_string);
            }
            catch { }
            return DateTime.Now;
        }


        /// <summary>
        /// 添加日志信息，日志文件编码为UTF8。如果其中LogType为Debug模式时，只有当Common.DebugMode为true时才会打印出来。
        /// </summary>
        /// <param name="log">日志内容。</param>
        /// <param name="logtype">默认值为System。</param>
        /// <param name="logfile">日志文件。</param>
        public static void AddLog(string log, LogType logtype = LogType.INFO, string logfile = "runtime.log")
        {
            try
            {
                // 注意：StackTrace的参数1非常重要，表示获得父一级函数调用的相关信息。 如果修改为0，则返回本身的行列信息，即TraceMethodInfo()函数的信息。
                var st = new System.Diagnostics.StackTrace(1, true).GetFrame(0);
                string info = string.Format("{1}.{2}:{3}",
                    st.GetFileName(),                       // 文件名
                    st.GetMethod().DeclaringType.FullName,  // 类全名
                    st.GetMethod().Name,                    // 方法名
                    st.GetFileLineNumber(),                 // 所在行号
                    st.GetFileColumnNumber());              // 所有列号

                if (logtype >= Common.LogLevel)
                {
                    using (StreamWriter sw = new StreamWriter(logfile, true, Encoding.UTF8))
                    {
                        sw.WriteLine($"{DateTime.Now:yyyyMMdd_HHmmss}\t{logtype}\t{info}\t{log}");
                    }
                }
            }
            catch { }
        }

        /// <summary>
        /// 从指定的Excel中提取表单，如果读取失败会写日志，同时返回为 null。
        /// </summary>
        /// <param name="inpath">输入Excel文件。</param>
        /// <param name="sheetName">表单的名称。</param>
        /// <returns></returns>
        public static Worksheet LoadWorkSheet(string inpath, string sheetName)
        {
            Worksheet sheet = null;
            Workbook workbook = new Workbook();
            try
            {
                workbook.LoadFromFile(inpath);
                if (workbook.Worksheets.Count == 0)
                    Error("文件索引错误，读取表单内容为空");
                else
                    sheet = workbook.Worksheets[sheetName]; // 读取成功后在这里赋值，此时才不为空。

            }
            catch (Exception ex1)
            {
                Error("文件读取失败，原因：" + ex1.Message);
            }
            finally
            {
                try
                {
                    // 关闭文件，可能不需要操作，先保留

                }
                catch (Exception ex2) { Error($"{ inpath} 文件关闭失败，原因：" + ex2.Message); }
            }

            return sheet;
        }

        /// <summary>
        /// 将表单的数据转为二维数组，以方便操作。
        /// </summary>
        /// <param name="ws"></param>
        /// <returns></returns>
        public static List<string[]> ToStringList(Worksheet ws)
        {
            var list = new List<string[]>();
            foreach (var row in ws.Rows)
            {
                string[] rowdata = new string[row.ColumnCount];
                for (int i = 0; i < row.ColumnCount; i++)
                    rowdata[i] = row.Columns[i].Value;
                list.Add(rowdata);
            }
            return list;
        }

        /// <summary>
        /// 对字符串进行加密。
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string Encrypt(string text)
        {
            if (string.IsNullOrEmpty(text))
                return "";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < text.Length; i++)
            {
                sb.Append((char)(text[i] + 3));
            }
            return sb.ToString();
        }

        /// <summary>
        /// 对字符串进行解密。
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string Decrypt(string text)
        {
            if (string.IsNullOrEmpty(text))
                return "";

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < text.Length; i++)
                sb.Append((char)(text[i] - 3));

            return sb.ToString();
        }

        /// <summary>
        /// 将输入转换为整型，如果解析失败，返回0。
        /// </summary>
        /// <param name="s">待解释字符串。</param>
        /// <param name="defaultValue">解释失败时的返回值，默认为0。</param>
        /// <returns></returns>
        public static int ParseInteger(string s, int defaultValue = 0)
        {
            return int.TryParse(s, out int v) ? v : defaultValue;
        }
    }
}
